#include "job.h"	

Job::Job(const char *name, long double inputData_tpt, long double outputData_tpt) {
	 if (inputData_tpt<=0) throw std::runtime_error("Application InputThroughput can not be equal or less than 0");
	 if (outputData_tpt<=0) throw std::runtime_error("Application OutputThroughput can not be equal or less than 0");	 

	this->name = new char [strlen(name)+1];
	this->name = strcpy((char *)this->name, name);
	this->numberOfTasks = 0;
	this->inputData_tpt = inputData_tpt;
	this->outputData_tpt = outputData_tpt;
	this->tasks = NULL;
}

Job::~Job() {
	if (this->name )  {
		delete this->name;
		this->name = NULL;
	}
	for (int n = 0; n < this->numberOfTasks; n++) {
			if (this->tasks[n]) {
				delete this->tasks[n];
				this->tasks[n] = NULL;		
			}
			
	}
	free(this->tasks);
	this->tasks = NULL;

}

void Job::addTask(long double inputDataSize, long double outputDataSize) {
	char *taskname = new char[64];
	
	this->numberOfTasks++;	

	sprintf(taskname, "task_%d", (this->numberOfTasks));
	this->tasks = (Task **) realloc(this->tasks, this->numberOfTasks*sizeof(Task *));

	this->tasks[this->numberOfTasks - 1] = new Task(taskname,this->inputData_tpt, this->outputData_tpt,inputDataSize, outputDataSize);
	
	delete taskname;

}

void Job::createReplicaOfTask(Task *taskToReplicate, long double replicaTransferCost) {
	taskToReplicate->replicate(replicaTransferCost);
}

void Job::createReplicaOfTask(int taskIndex, long double replicaTransferCost) {
	this->createReplicaOfTask(this->tasks[taskIndex], replicaTransferCost);
}

Task *Job::getTaskByIndex(int taskIndex) {
	return  this->tasks[taskIndex];
}

Task *Job::getTaskByName(const char *task_name) {
	Task *task = NULL;
	for (int n = 0; n<this->numberOfTasks; n++) {
		if (!strcmp(task_name, this->getTaskByIndex(n)->getName())) {
			task = this->getTaskByIndex(n);
		}
	}
	
	if (task == NULL) throw std::runtime_error("Task does not exist!");
	return task;
}

int Job::getNumberOfTasks() {
	return this->numberOfTasks;
}

long double Job::getInputTpt() {
	return this->inputData_tpt;
}

long double Job::getOutputTpt() {
	return this->outputData_tpt;
}

const char* Job::getName() {
	return this->name;
}

void Job::resetTasks() {
	for (int n = 0; n < this->numberOfTasks; n++) {
			this->tasks[n]->clearReplicas();		
			this->tasks[n]->restoreInputTransferCost();		
	}
}

bool Job::hasTaskNotScheduled() {
	bool res = false;
	for (int n = 0; n < this->getNumberOfTasks(); n++) {
		if (this->getTaskByIndex(n)->notScheduled()) {
			res = true;
			break;
		}
	}
	return res;
}

bool Job::hasTaskNotFinished() {
	bool res = false;
	for (int n = 0; n < this->getNumberOfTasks(); n++) {
		if (!this->getTaskByIndex(n)->finished()) {
			res = true;
			break;
		}
	}
	return res;
}

int Job::getMinReplicationDegree() {
	int minReplicationDegree = LONG_MAX;
	
	for (int t = 0; t < this->getNumberOfTasks(); t++) {
		
		if ((this->getTaskByIndex(t)->isRunning()) && (this->getTaskByIndex(t)->getNumberOfReplicas() < minReplicationDegree)) {
			minReplicationDegree = this->getTaskByIndex(t)->getNumberOfReplicas();
		}
		
	}
	
	return minReplicationDegree;
}

void Job::deleteAllTasks() {
	for (int t = 0; t < this->numberOfTasks; t++) {
		if (this->tasks[t]!=NULL) delete this->tasks[t];
		this->tasks[t] = NULL;
	}
	
	if (this->tasks!= NULL) free(this->tasks);
	this->tasks = NULL;
	
	this->numberOfTasks = 0;
	
}
